#include "Coordinator.h"
#include "hw/Button.h"
#include "util.h"

#include "hw/RPi_GPIO.h"
#include "hw/Samsung_20T202DA2JA.h"
//#include "hw/LedControl.h"

//debug
#include <iostream>

/** loop delay msec */
#define DELAY_COORD 50
/** loop delay when sleeping msec, save energy */
#define DELAY_COORD_STANDBY 250

/** msec delay to start streaming, avoids troubles when switching very fast */
#define DELAY_CHANGE_MODE 500
/** Without such delay, they interfer with each other, msec 
for example power of amp get enabled, but raspbery pi is not set as input */
#define DELAY_AFTER_RELAY_SET 200/*100*/

/** GPIO read values mapped to meaning */
#define VALUE_AUX_IN_PLUGGED 1
#define VALUE_ONOFF_ENABLED 1
#define VALUE_NET_MODE_ROUTER 1
#define VALUE_MODE_WEB_RADIO 1

#define AMP_POWER_ON 1
#define AMP_POWER_OFF 0

#define CLI_UPNP_DLNA_START "/etc/init.d/gmediarenderer start"
#define CLI_UPNP_DLNA_STOP "/etc/init.d/gmediarenderer stop"


int Coordinator::threadHandler(){
    LOG << "Start Coordinator" << std::endl;

    m_wifi_ctrl->start();
    ////program main loop
    std::shared_ptr<ButtonEvent> e = 0;
    std::shared_ptr<Message> m = 0;
    while (m_shouldRun){
        m = m_q.deque();
        while (m != 0 && m_shouldRun){
            if (ButtonEvent::MESSAGE_TYPE == m->message_type()){
                handleEvent(std::static_pointer_cast<ButtonEvent>(m));
            }else{
                handleMessage(m);
            }
            //process until Q empty
            m = m_q.deque();
        }
        
        //handle mode change
        if (m_change_mode_flag){
            std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
            std::chrono::milliseconds d = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_change_mode_tp);
            if (d.count() > DELAY_CHANGE_MODE){
                change_mode();
                m_change_mode_flag = false;
            }
        }
        //sleep
        if (m_current_state == STATE_STANDBY)
            std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_COORD_STANDBY));
        std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_COORD));
    }
    if (m_active_module != 0)
        m_active_module->stop();
    LOG << "End Coordinator" << std::endl;
    return 0;
}

int Coordinator::init(){
    m_value_aux_in_1 = -1;
    m_value_aux_in_2 = -1;
    m_value_onoff = -1;
    m_value_switch_mode = -1;
    m_value_switch_net_mode = NET_STATE_NULL;
    
    m_active_module = 0;
    m_current_state = STATE_NULL;
    
    m_change_mode_flag = false;
    
    //TODO : make a class for Relay control
    RPi_GPIO::exportPin(GPIO_RELAY_POWER);
    RPi_GPIO::setOutput(GPIO_RELAY_POWER);
    RPi_GPIO::exportPin(GPIO_RELAY_0);
    RPi_GPIO::setOutput(GPIO_RELAY_0);
    RPi_GPIO::exportPin(GPIO_RELAY_1);
    RPi_GPIO::setOutput(GPIO_RELAY_1);
    
    //blue blinking LED
    //std::shared_ptr<LedControl> led_ctrl(new LedControl(GPIO_FRONT_LED, 1500, 20));
    //led_ctrl->enable();
    //led_ctrl->start();
    
    //modules
    m_web_radio = std::shared_ptr<WebRadio>(new WebRadio(m_screen));
    m_standby_ctrl = std::shared_ptr<StandbyControl>(new StandbyControl(m_screen));
    m_wifi_ctrl = std::shared_ptr<WifiControl>(new WifiControl(this));
    
    return 0;
}

int Coordinator::handleEvent(std::shared_ptr<ButtonEvent> e){
    if (e->origin == GPIO_AUX_1){
        LOG << "+++ Coordinator aux 1 in " << e->value << std::endl;
        //store new value
        m_value_aux_in_1 = e->value;
        //set flag for mode to be changed
        m_change_mode_flag = true;
        m_change_mode_tp = std::chrono::high_resolution_clock::now();
    }else if (e->origin == GPIO_AUX_2){
        LOG << "+++ Coordinator aux 2 in " << e->value << std::endl;
        //store new value
        m_value_aux_in_2 = e->value;
        //set flag for mode to be changed
        m_change_mode_flag = true;
        m_change_mode_tp = std::chrono::high_resolution_clock::now();
    }else if (e->origin == GPIO_ONOFF){
        LOG << "+++ Coordinator ONOFF " << e->value << std::endl;
        //store new value
        m_value_onoff = e->value;
        //set flag for mode to be changed
        m_change_mode_flag = true;
        m_change_mode_tp = std::chrono::high_resolution_clock::now();
    }else if (e->origin == GPIO_SWITCH_NET_MODE){
        LOG << "+++ Coordinator net mode " << e->value << std::endl;
        //store new value
        m_value_switch_net_mode = e->value;
        //set flag for mode to be changed
        m_change_mode_flag = true;
        m_change_mode_tp = std::chrono::high_resolution_clock::now();
    }else if (e->origin == GPIO_SWITCH_MODE){
        LOG << "+++ Coordinator mode " << e->value << std::endl;
        //store new value
        m_value_switch_mode = e->value;
        //set flag for mode to be changed
        m_change_mode_flag = true;
        m_change_mode_tp = std::chrono::high_resolution_clock::now();
    }else if (e->origin == GPIO_BUTTON_DOWN){
        LOG << "+++ Coordinator DOWN " << e->value << std::endl;
        if (e->type == input::EVENT_RELEASED && m_active_module == m_web_radio){
            m_web_radio->next();
        }
    }else if (e->origin == GPIO_BUTTON_UP){
        LOG << "+++ Coordinator UP" << std::endl;
        if (e->type == input::EVENT_RELEASED && m_active_module == m_web_radio){
            m_web_radio->previous();
        }
    }
    
    return 0;
}

int Coordinator::change_mode(){
    //deal with input states according to priority
    //ON OFF
    if (m_value_onoff == VALUE_ONOFF_ENABLED){
        if (m_current_state != STATE_STANDBY){
            m_current_state = STATE_STANDBY;
            //stop a module if any started
            if (m_active_module != 0){
                m_active_module->stop();
                m_active_module = 0;
            }
            LOG << "COORDINATOR STATE : OFF" << std::endl;
            m_wifi_ctrl->wireless_disable();
            m_active_module = m_standby_ctrl;
            m_active_module->start();
            
            amp_power_off();
            pause_buttons();
        }
    }
    else{
        //from here, it's not standby mode
        if (m_current_state == STATE_STANDBY || m_current_state == STATE_NULL){
            //current state is standby, meaning it's waking up from standby or just starting up
            m_wifi_ctrl->wireless_enable();
            resume_buttons();
            amp_power_on();
            
            m_current_net_state = NET_STATE_NULL;
            
        }    
            
        //Aux 1
        if (m_value_aux_in_1 == VALUE_AUX_IN_PLUGGED){
            if (m_current_state != STATE_AUX_1){
                m_current_state = STATE_AUX_1;
                //stop a module if any started
                if (m_active_module != 0){
                    m_active_module->stop();
                    m_active_module = 0;
                }
                LOG << "COORDINATOR STATE : Aux In 1" << std::endl;
                m_screen->clear();
                m_screen->write("       Aux In");
                RPi_GPIO::write(GPIO_RELAY_0, 0);
                std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
                RPi_GPIO::write(GPIO_RELAY_1, 0);
                std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
            }
        }
        //Aux 2 (3.5mm input more important than 6.5mm... coz I said so !)
        else if (m_value_aux_in_2 == VALUE_AUX_IN_PLUGGED){
            if (m_current_state != STATE_AUX_2){
                m_current_state = STATE_AUX_2;
                //stop a module if any started
                if (m_active_module != 0){
                    m_active_module->stop();
                    m_active_module = 0;
                }
                LOG << "COORDINATOR STATE : Aux In 2" << std::endl;
                m_screen->clear();
                m_screen->write("     Guitar In");
                RPi_GPIO::write(GPIO_RELAY_0, 1);
                std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
                RPi_GPIO::write(GPIO_RELAY_1, 0);
                std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
            }
        }
        //Mode : WebRadio / Remote
        else if (m_value_switch_mode == VALUE_MODE_WEB_RADIO){
            if (m_current_state != STATE_WEB_RADIO){
                //stop gmedia renderer... if needed
                //... NOT GOOD coz should be done in other places
                // ?????????? Re-DO a module for each mode ?? to get a ->stop() for all ??
                if (m_current_state == STATE_REMOTE){
                    Util::exec_read_bash(CLI_UPNP_DLNA_STOP);
                }
                
                m_current_state = STATE_WEB_RADIO;
                //stop a module if any started
                if (m_active_module != 0){
                    m_active_module->stop();
                    m_active_module = 0;
                }
                
                LOG << "COORDINATOR STATE : WebRadio" << std::endl;
                m_active_module = m_web_radio;
                m_active_module->start();
                //set relays
                RPi_GPIO::write(GPIO_RELAY_0, 0);
                std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
                RPi_GPIO::write(GPIO_RELAY_1, 1);
                std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
            }
        }else{
            if (m_current_state != STATE_REMOTE){
                m_current_state = STATE_REMOTE;
                //stop a module if any started
                if (m_active_module != 0){
                    m_active_module->stop();
                    m_active_module = 0;
                }
                LOG << "COORDINATOR STATE : Remote" << std::endl;
                m_screen->clear();
                m_screen->write("       Remote");
                
                RPi_GPIO::write(GPIO_RELAY_0, 0);
                std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
                RPi_GPIO::write(GPIO_RELAY_1, 1);
                std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
                //start gmedia renderer
                Util::Util::exec_read_bash(CLI_UPNP_DLNA_START);
            }
        }
    }
    
    ////Net Mode : router / client
    //this is independent from other states
    if (m_value_switch_net_mode == VALUE_NET_MODE_ROUTER){
        if (m_current_net_state != NET_STATE_ROUTER){
            LOG << "NET STATE : Router Mode" << std::endl;
            m_wifi_ctrl->set_as_router();
            m_current_net_state = NET_STATE_ROUTER;
        }
    }else{
        if (m_current_net_state != NET_STATE_CLIENT){
            LOG << "NET STATE : client Mode" << std::endl;
            m_wifi_ctrl->set_as_client();
            m_current_net_state = NET_STATE_CLIENT;
        }
    }
    return 0;
}

int Coordinator::handleMessage(std::shared_ptr<Message> m){
    if (msg::JOINED_WIFI_NET == m->cmd || msg::CREATED_WIFI_NET == m->cmd){
        if (STATE_WEB_RADIO == m_current_state){
            m_web_radio->reload_current_stream();
        }else if (STATE_REMOTE == m_current_state){
            Util::exec_read_bash(CLI_UPNP_DLNA_STOP);
            Util::exec_read_bash(CLI_UPNP_DLNA_START);
        }
    }else if (msg::NO_KNOWN_ESSID == m->cmd) {
        m_screen->clear();
        m_screen->write("No known wifi found");
    }
}

void Coordinator::pause_buttons(){
    for (int i=0; i<m_button_list->size(); i++){
        m_button_list->at(i)->pause();
    }
}

void Coordinator::resume_buttons(){
    for (int i=0; i<m_button_list->size(); i++){
        m_button_list->at(i)->resume();
    }
}

void Coordinator::amp_power_on(){
    RPi_GPIO::write(GPIO_RELAY_POWER, AMP_POWER_ON);
    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
}

void Coordinator::amp_power_off(){
    RPi_GPIO::write(GPIO_RELAY_POWER, AMP_POWER_OFF);
    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_AFTER_RELAY_SET));
}


